package com.kamistoat.meimeichat.meimeichatclient.kcp;

import com.kamistoat.meimeichat.meimeichatclient.application.UIServiceConnector;
import com.kamistoat.meimeichat.meimeichatclient.config.NettyClientConstant;
import com.kamistoat.meimeichat.meimeichatclient.infrastructure.util.BeanUtil;
import com.kamistoat.meimeichat.meimeichatclient.socket.NettyClient;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioDatagramChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetSocketAddress;
import java.util.concurrent.Callable;

/**
 * UDP链接使用的Netty客户端，用于建立和其他客户端之间的P2P通信
 */
public class KcpNettyClient implements Callable<ChannelFuture> {
    private Logger logger = LoggerFactory.getLogger(NettyClient.class);

    // 当建立P2P时，不存在客户端和服务端的概念。由于UDP链接的特性，因此只需要使用一个Loop
    private final NioEventLoopGroup workGroup = new NioEventLoopGroup();
    // 额外定义的 ChannelFuture 变量，用于记录 Bootstrap.connect() 返回的异步操作句柄，用以检测异步操作是否执行完毕/成功
    private ChannelFuture channelFuture;
    // NettyUDP监听端口
    private final Integer PORT = NettyClientConstant.NETTY_P2P_LISTEN_PORT;

    private UIServiceConnector uiServiceConnector;

    public KcpNettyClient(UIServiceConnector uiServiceConnector) {
        this.uiServiceConnector = uiServiceConnector;
    }

    @Override
    public ChannelFuture call() throws Exception {
        try {
            // 不存在客户端服务端的概念，因此全部以服务端对待即可。(也有全部以客户端对待的例子)
            Bootstrap bootstrap = new Bootstrap();
            bootstrap
                    .group(workGroup)
                    .channel(NioDatagramChannel.class)
                    .option(ChannelOption.SO_BROADCAST, true)
                    .handler(new KcpNettyClientChannelInitializer(this.uiServiceConnector));
            // 建立UDP监听
            this.channelFuture = bootstrap.bind(new InetSocketAddress(PORT)).syncUninterruptibly();
            BeanUtil.addBean("ClientP2PChannel", this.channelFuture.channel());
        } catch (Exception exception) {
            logger.error("Netty Client P2P Listener Start Error: " + exception.getMessage());
        } finally {
            if (this.channelFuture != null && this.channelFuture.isSuccess()) {
                logger.info("Netty Client P2P Listener Start Success");
            } else {
                destory();
                logger.error("Netty Client P2P Listener Closed");
            }
        }
        return this.channelFuture;
    }

    public boolean isActive() {
        return this.channelFuture.channel().isActive();
    }

    public void destory() {
        this.workGroup.shutdownGracefully();
    }

    public ChannelFuture channelFuture() {
        return this.channelFuture;
    }
}
























